
public abstract class Data implements Comparable<Data>{
	// Null
	// Pair
	// String
	
	public boolean isTrue() { return this == Evaluator.SCHAVA_TRUE; }
	public boolean isNull() { return this == Evaluator.THE_EMPTY_LIST; }
	public boolean isAtom() { return false; }
	public boolean isPair() { return false; }
	public boolean isList() { return false; }
	public boolean isNumber() { return false; }
	public boolean isBoolean() { return false; }
	public boolean isString() { return false; }
	public boolean isSymbol() { return false; }
	public boolean isEmpty() { return false; }
	public boolean equals(Object other) { return false; }
	
	public boolean isProcedure() {
		return false;
	}
	
	public Data car() {
		if (isPair()) {
			return ((Pair)this).car();
		}
		throw new SchavaException("car: Argument is not a pair: " + this);
	}
	
	public Data cdr() {
		if (isPair()) {
			return ((Pair)this).cdr();
		}
		throw new SchavaException("cdr: Argument is not a pair: " + this);
	}
	
	public Data operator() {
		return car();
	}
	
	public Data operands() {
		return cdr();
	}
	
	public boolean isSelfEvaluating() {
		return isNumber() || isString() || isBoolean();
	}
	
	public boolean isVariable() {
		return isSymbol();
	}

	public boolean isAssignment() {
		return isPair() && this.isTagged("set!");
	}

	public boolean isSetCar() {
		return isPair() && this.isTagged("set-car!");
	}

	public boolean isSetCdr() {
		return isPair() && this.isTagged("set-cdr!");
	}

	public void setCar(Data _car) {
		((Pair)this).setCar(_car);
	}


	public void setCdr(Data _cdr) {
		((Pair)this).setCdr(_cdr);
	}

	public boolean isDefinition() {
		return isPair() && this.isTagged("define");
	}
	
	public boolean isQuoted() {
		return isPair() && this.isTagged("quote");
	}
	public boolean isLambda() {
		return isPair() && this.isTagged("lambda");
	}
	
	public boolean isIf() {
		return isPair() && this.isTagged("if");
	}
	
	public boolean isCond() {
		return isPair() && this.isTagged("cond");
	}

	public boolean isElseClause() {
		return isPair() && this.isTagged("else");
	}
	
	public boolean isBegin() {
		return isPair() && this.isTagged("begin");
	}
	
	public boolean isLet() {
		return isPair() && this.isTagged("let");
	}
	
	public boolean isApply() {
		return isPair() && this.isTagged("apply");		
	}
	
	public boolean isThunk() {
		return isPair() && this.isTagged("thunk");
	}
	
	public Data getAssignmentVariable() {
		return cdr().car();
	}
	
	public Data getAssignmentArgument() {
		return cdr().cdr().car();
	}
	
	public Data getLambdaBodySequence() {
		return cdr().cdr();
	}
	
	public Object getValue() {
		return null;
	}
	
	public Data textOfQuotation() {
		return cdr().car();
	}
	
	public Data condClauses() {
		return cdr();
	}
	
	public Data condPredicate() {
		return car();
	}
	
	public Data condActions() {
		return cdr();
	}
	
	public Data sequenceToExpression() {
		if (isNull()) {
			return Evaluator.THE_EMPTY_LIST;
		} else if (cdr().isNull()){
			return car();
		} else {
			return Primitive.cons("begin", this);
		}
	}
	
	public Data letBindings() {
		return cdr().car();
	}
	
	public Data letBodySequence() {
		return cdr().cdr();
	}
	
	public Data bindingVariable() {
		return car();
	}
	
	public Data bindingExpression() {
		return cdr().car();
	}

	
	public Data beginActions() {
		return cdr();
	}
	
	public boolean isTagged(String tag) {
		return car().toString().equals(tag);
	}
	
	public boolean isApplication() {
		return isPair();
	}
	
	
	public abstract String toString();
	

	public boolean isPrimitiveProcedure() {
		return isTagged("primitive");
	}
	
	public boolean isCompoundProcedure() {
		return isTagged("procedure");
	}
	
	/** 
	 * Returns a Scava List of parameters
	 * from a list of (parameter argument)
	 * lists from a let.
	 * @return
	 */
	public Data getLetParameters() {
		if (isNull()) {
			return Evaluator.THE_EMPTY_LIST;
		} else {
			return Primitive.cons(car().car(), cdr().getLetParameters());
		}
	}
	
	/**
	 * Returns a Scava list of arguments from a list
	 * of (parameter argument) lists in a Let
	 * @return
	 */
	public Data getLetArguments() {
		if (isNull()) {
			return Evaluator.THE_EMPTY_LIST;
		} else {
			return Primitive.cons(car().cdr().car(), 
					cdr().getLetArguments());
		}
	}
	
	public Data getApplyOp() {
		return cdr().car();
	}
	
	public Data getApplyExp() {
		return cdr().cdr().car();
	}

	public Data getThunkExp() {
		return cdr().car();
	}
	
	public Environment getThunkEnv() {
		return (Environment)cdr().cdr().car();
	}
}
